﻿using Fluent.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Drawing;
using System.Collections.ObjectModel;

namespace Fluent
{
    public class Browser:IWebDriver
    {
        private IWebDriver _driver;
        public TestContext TestContext { get; private set; }
        public EBrowserType Type { get; private set; }

        public Browser(IWebDriver driver, EBrowserType type)
        {
            this._driver = driver;
            this.Type = type;
        }

        public ISearchContext SearchContext
        {
            get { return this.GetDriver() as ISearchContext; }
        }

        public IWebDriver Driver
        {
            get
            {
                return _driver;
            }
        }

        public IWebDriver GetDriver()
        {
            return this._driver;
        }

        public string Title
        {
            get { return this.GetDriver().Title; }
        }

        public Uri URL
        {
            get { return new Uri(this.GetDriver().Url); }
        }

        public Browser PlugIn(Func<WebPageBase> pageFactory)
        {

            return this;
        }

        public Browser SetTestContext(TestContext context)
        {
            this.TestContext = context;

            return this;
        }

        public Browser Navigate(string url)
        {
            this.GetDriver().Navigate().GoToUrl(url);
            Log.Information("Navigate URL to {0}", url);

            return this;
        }

        public Browser ReSize(double width, double height)
        {
            throw new NotImplementedException();
        }

        public Browser Snapshot(SnapshotType type)
        {
            if (this.TestContext == null)
            {
                throw new ArgumentNullException("TestContext can't be null! You could trying Snapshot(SnapshotType, string, string, ImageFormat) to get more controls.");
            }

            var imageFullPath = this.Snapshot(type, this.TestContext.TestResultsDirectory,
                fileName: string.Format("{0}_{1}", this.TestContext.TestName, DateTime.Now.ToString("MMddHHmmss")),
                imageFormat: ImageFormat.Png);

            TestContext.AddResultFile(imageFullPath);

            return this;
        }

        public string Snapshot(SnapshotType type, string storeFolderPath, string fileName, ImageFormat imageFormat)
        {
            var storeFullPath = string.Empty;
            try
            {
                if (!Directory.Exists(storeFolderPath))
                {
                    Directory.CreateDirectory(storeFolderPath);
                }

                storeFullPath = Path.Combine(storeFolderPath, fileName + "." + imageFormat.ToString());

                if (type == SnapshotType.FullPage)
                {
                    // Known Issue 294:	Chrome & IE - Take screenshot is not full page
                    // https://code.google.com/p/chromedriver/issues/detail?id=294
                    (this.GetDriver() as ITakesScreenshot).GetScreenshot().SaveAsFile(storeFullPath, imageFormat);
                }

                Log.Information("Take browser snapshot at {0}", storeFullPath);
            }
            catch (Exception ex)
            {
                Log.Error("Take Screenshot error! exception: {0}", ex.Message);
            }

            return storeFullPath;
        }

        public Bitmap Render(double x, double y, double width, double height, double offsetX, double offsetY, double offsetW, double offsetH)
        {
            Bitmap bit = new Bitmap((int)(Math.Floor(width)), (int)(Math.Floor(width)));

            using (var grah = Graphics.FromImage(new Bitmap((int)(Math.Floor(width)), (int)(Math.Floor(width)))))
            {
                grah.CopyFromScreen((int)(Math.Floor(x)), (int)(Math.Floor(y)), 0, 0, bit.Size);
            }

            return bit;
        }

        public Browser Scroll(int x, int y)
        {
            return this.Scroll(x.ToString(CultureInfo.InvariantCulture), y.ToString(CultureInfo.InvariantCulture));
        }

        public Browser Scroll(string x, string y)
        {
            Log.Information("Scroll point to: [{0}, {1}]", x, y);
            this.ExecuteJS("window.scrollTo({0},{1});", x, y);

            return this;
        }

        internal Browser ExecuteJS(string scriptFormatString, params object[] args)
        {
            (this.GetDriver() as IJavaScriptExecutor).ExecuteScript(string.Format(scriptFormatString, args));

            return this;
        }

        internal object ExecuteJS<TReturn>(string scriptFormatString, params object[] args)
        {
            var objResult = (this.GetDriver() as IJavaScriptExecutor).ExecuteScript(scriptFormatString);

            return objResult;
        }

        public void Close()
        {
            this.GetDriver().Close();
        }

        public void Quit()
        {
            this.GetDriver().Quit();
            this._driver = null;
        }

        public TResult Wait<TResult>(Func<IWebDriver, TResult> condition,
            double timeOut = default(double),
            double sleepInterval = default(double),
            string errorMessage = "")
        {
            TResult result = default(TResult);
            WebDriverWait waitAPI;

            if (sleepInterval != default(double))
            {
                waitAPI = new WebDriverWait(new SystemClock(),
                    this.GetDriver(),
                    timeOut == default(double) ? TimeSpan.FromMilliseconds(120000) : TimeSpan.FromMilliseconds(timeOut),
                    TimeSpan.FromMilliseconds(sleepInterval));
            }
            else
            {
                waitAPI = new WebDriverWait(this.GetDriver(),
                     timeOut == default(double) ? TimeSpan.FromMilliseconds(120000) : TimeSpan.FromMilliseconds(timeOut));
            }

            try
            {
                result = waitAPI.Until<TResult>(condition);
            }
            catch (WebDriverTimeoutException timeoutEx)
            {
                Log.Information(timeoutEx.Message);
            }
            catch (WebDriverException webDriverEx)
            {
                Log.Information(webDriverEx.Message);
            }
            catch (Exception ex)
            {
                if (!string.IsNullOrWhiteSpace(errorMessage))
                {
                    throw new Exception(errorMessage, ex);
                }
            }

            return result;
        }

        public IWebElement FindElement(By by)
        {
            return _driver.FindElement(by);
        }

        public ReadOnlyCollection<IWebElement> FindElements(By by)
        {
            return _driver.FindElements(by);
        }
        
        public string CurrentWindowHandle
        {
            get { return _driver.CurrentWindowHandle; }
        }

        public IOptions Manage()
        {
            return _driver.Manage();
        }

        public INavigation Navigate()
        {
            return _driver.Navigate();
        }

        public string PageSource
        {
            get { return _driver.PageSource; }
        }

        public ITargetLocator SwitchTo()
        {
            return _driver.SwitchTo();
        }

        public string Url
        {
            get
            {
                return _driver.Url;
            }
            set
            {
                _driver.Url = value;
            }
        }

        public ReadOnlyCollection<string> WindowHandles
        {
            get { return _driver.WindowHandles; }
        }

        public void Dispose()
        {
            _driver.Dispose();
        }
    }

    public enum SnapshotType
    {
        FullPage
    }
}